#include "include.h"
#include "common/utils.h"

#include "fparser.h"

#include <boost/regex.hpp>


#include "compile/debug_memoryleaks"

/**
* Math functions.
*/


/**
 * Rounds the given double to the next int.
 */
int Math::round(double v) {
    return (v-floor(v)<0.5 ? (int)floor(v) : (int)ceil(v));
}


/**
 * Save pow function
 */
double Math::pow(double x, double y) {
	errno = 0;
	double ret = ::pow(x, y);
	if (errno==EDOM) {
//        RS_DEBUG->print(RS_Debug::D_ERROR,"RS_Math::pow: EDOM in pow");
		ret = 0.0;
	}
	else if (errno==ERANGE) {
//        RS_DEBUG->print(RS_Debug::D_WARNING,"RS_Math::pow: ERANGE in pow");
		ret = 0.0;
	}
	return ret;
}


/**
 * Converts radians to degrees.
 */
double Math::rad2deg(double a) {
    return (a/(2.0*PI)*360.0);
}



/**
 * Converts degrees to radians.
 */
double Math::deg2rad(double a) {
    return ((a/360.0)*(2.0*PI));
}



/**
 * Converts radians to gradians.
 */
double Math::rad2gra(double a) {
    return (a/(2.0*PI)*400.0);
}



/**
 * Finds greatest common divider using Euclid's algorithm.
 */
int Math::findGCD(int a, int b) {
    int rem;

    while (b!=0) {
        rem = a % b;
        a = b;
        b = rem;
    }

    return a;
}



/**
 * Tests if angle a is between a1 and a2. a, a1 and a2 must be in the
 * range between 0 and 2*PI.
 * All angles in rad.
 *
 * @param reversed true for clockwise testing. false for ccw testing.
 * @return true if the angle a is between a1 and a2.
 */
bool Math::isAngleBetween(double a,
                             double a1, double a2,
                             bool reversed) {

    bool ret = false;

    if (reversed) {
        double tmp = a1;
        a1 = a2;
        a2 = tmp;
    }

    if(a1>=a2-1.0e-12) {
        if(a>=a1-1.0e-12 || a<=a2+1.0e-12) {
            ret = true;
        }
    } else {
        if(a>=a1-1.0e-12 && a<=a2+1.0e-12) {
            ret = true;
        }
    }
    //RS_DEBUG->print("angle %f is %sbetween %f and %f",
    //                a, ret ? "" : "not ", a1, a2);
    return ret;
}



/**
 * Corrects the given angle to the range of 0-2*Pi.
 */
double Math::correctAngle(double a) {
    while (a>2*PI)
        a-=2*PI;
    while (a<0)
        a+=2*PI;
    return a;
}



/**
 * @return The angle that needs to be added to a1 to reach a2.
 *         Always positive and less than 2*pi.
 */
double Math::getAngleDifference(double a1, double a2) {
    double ret;

    if (a1>=a2) {
        a2+=2*PI;
    }
    ret = a2-a1;

    if (ret>=2*PI) {
        ret=0.0;
    }

    return ret;
}


/**
* Makes a text constructed with the given angle readable. Used
* for dimension texts and for mirroring texts.
*
* @param readable true: make angle readable, false: unreadable
* @param corrected Will point to true if the given angle was
*   corrected, false otherwise.
*
 * @return The given angle or the given angle+PI, depending which on
 * is readable from the bottom or right.
 */
double Math::makeAngleReadable(double angle, bool readable,
                                  bool* corrected) {

    double ret;

    bool cor = isAngleReadable(angle) ^ readable;

    // quadrant 1 & 4
    if (!cor) {
        ret = angle;
    }
    // quadrant 2 & 3
    else {
        ret = angle+PI;
    }

    if (corrected!=NULL) {
        *corrected = cor;
    }

    return ret;
}


/**
 * @return true: if the given angle is in a range that is readable
 * for texts created with that angle.
 */
bool Math::isAngleReadable(double angle) {
    if (angle>PI/2.0*3.0+0.001 ||
            angle<PI/2.0+0.001) {
        return true;
    } else {
        return false;
    }
}



/**
 * @param tol Tolerance in rad.
 * @retval true The two angles point in the same direction.
 */
bool Math::isSameDirection(double dir1, double dir2, double tol) {
	double diff = fabs(dir1-dir2);
	if (diff<tol || diff>2*PI-tol) {
		//std::cout << "RS_Math::isSameDirection: " << dir1 << " and " << dir2
		//	<< " point in the same direction" << "\n";
		return true;
	}
	else {
		//std::cout << "RS_Math::isSameDirection: " << dir1 << " and " << dir2
		//	<< " don't point in the same direction" << "\n";
		return false;
	}
}


/**
 * Compares two double values with a tolerance.
 */
bool Math::cmpDouble(double v1, double v2, double tol) {
    return (fabs(v2-v1)<tol);
}



/**
 * Evaluates a mathematical expression and returns the result.
 * If an error occured, the given default value 'def' will be returned.
 */
double Math::eval(const std::string& expr, double def) {

    bool ok;
    double res = Math::eval(expr, &ok);

    if (!ok) {
        //std::cerr << "RS_Math::evaluate: Parse error at col "
        //<< ret << ": " << fp.ErrorMsg() << "\n";
        return def;
    }

    return res;
}



double Math::eval(const std::string& expr, bool* ok)
{
    if (expr.empty()) {
        if (ok!=NULL) {
            *ok = false;
        }
        return 0.0;
    }

    FunctionParser fp;
    fp.AddConstant("pi", PI);

    // replace '14 3/4' with '14+3/4'
    std::string s = expr;

    //bool done;
    //do {
    //    done = true;
    //    int i = s.find(RS_RegExp("[0-9]* [0-9]*/[0-9]*"));
    //    if (i!=-1) {
    //        int i2 = s.find(' ', i);
    //        if (i2!=-1) {
    //            s.replace(i2, 1, "+");
    //            done = false;
    //        }
    //    }
    //} while (!done);

	boost::regex e("([0-9]+\\s*) ([0-9]+/[0-9]+)");
	std::string fmt("$1+$2");
	std::string ss = boost::regex_replace(s, e, fmt);

	///

    int ret = fp.Parse(ss, "", true);

    if (ret>=0) {
        if (ok!=NULL) {
            *ok = false;
        }
        return 0.0;
    }

    if (ok!=NULL) {
        *ok = true;
    }

    return fp.Eval(NULL);
}


////////////////////////////////////////////////////////////////////////////////

/**
* Text functions
*/


std::string Text::itoa(const int i)
{
	char s[100];
	sprintf(s, "%d", i);
	return s;
}

std::string Text::ftoa(const double f)
{
	char s[100];
	sprintf(s, "%1.2f", f);
	return s;
}

const int Text::atoi(std::string s)
{
	return ::atoi(s.c_str());
}

const double Text::atof(std::string s)
{
	return ::atof(s.c_str());
}





////////////////////////////////////////////////////////////////////////////////


// EOF

